package com.huawei.app.demo.video;

import java.io.IOException;
import java.nio.ByteBuffer;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.view.Surface;

import com.huawei.app.demo.util.LogUtils;

/**
 * 编解码任务
 *
 * @since 2021-01-18
 */
public class AvcEncoderDecoderTask implements Runnable {

    private static final String TAG = AvcEncoderDecoderTask.class.getSimpleName();

    public boolean isRuning = true;
    private MediaCodec mediaCodecEncoder; // 编码器
    private MediaCodec mediaCodecDecoder; // 解码器
    private Surface inputSurface;         // 编码器的输入Surface

    private int width;              // 宽度
    private int height;             // 高度
    private int framerate;          // 帧率
    private int bitrate = 12000000; // 码率
    private int TIMEOUT_USEC = 12000;

    public AvcEncoderDecoderTask(int width, int height, int framerate) {
        this.width = width;
        this.height = height;
        this.framerate = framerate;
    }

    /**
     * 初始化编码器
     * @return
     */
    public Surface initEncoder() {
        try {
            // 初始化编码器
            mediaCodecEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
            // mediaCodecEncoder = MediaCodec.createByCodecName("OMX.google.h264.encoder");
            // mediaCodecEncoder = MediaCodec.createByCodecName("OMX.hisi.video.encoder.avc");
            MediaFormat mediaFormatEncoder = createMediaFormat(MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
            mediaCodecEncoder.configure(mediaFormatEncoder, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            inputSurface = mediaCodecEncoder.createInputSurface();
            mediaCodecEncoder.start();
        } catch (IOException e) {
            LogUtils.getInstance().e(TAG, e.getMessage());
        }
        return inputSurface;
    }

    /**
     * 初始化解码器
     * @param outputSurface
     */
    public void initDecoder(Surface outputSurface) {
        try {
            // 初始化解码器
            mediaCodecDecoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
            // mediaCodecDecoder = MediaCodec.createByCodecName("OMX.google.h264.decoder");
            // mediaCodecDecoder = MediaCodec.createByCodecName("OMX.hisi.video.decoder.avc");
            MediaFormat mediaFormatDecoder = createMediaFormat(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            mediaCodecDecoder.configure(mediaFormatDecoder, outputSurface, null, 0);
            mediaCodecDecoder.start();
        } catch (Exception e) {
            LogUtils.getInstance().e(TAG, e.getMessage());
        }
    }

    private MediaFormat createMediaFormat(int keyColorFormat) {
        MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, keyColorFormat); // 颜色格式

        // 码率模式
        // CQ 不控制码率, 尽最大可能保证图像质量
        // CBR 静态码率
        // VBR 动态码率, 根据图像内容的复杂度来动态调整输出码率, 图像复杂则码率高, 图像简单则码率低
        mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); // 码率
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate); // 帧率
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 帧间间隔, 单位秒
        mediaFormat.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); // H264 BP
        mediaFormat.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel41);

        LogUtils.getInstance().i(TAG, "createMediaFormat "
                + " profile: " + mediaFormat.getInteger(MediaFormat.KEY_PROFILE) + ", "
                + " level: " + mediaFormat.getInteger(MediaFormat.KEY_LEVEL));
        return mediaFormat;
    }

    @Override
    public void run() {
        Thread.currentThread().setName(this.getClass().getSimpleName());

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        byte[] imgDataOut;
        long frameIndex = 0;
        while (isRuning) {
            try {
                // 获取编码后的数据
                int outputBufferIndex = mediaCodecEncoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                while (outputBufferIndex >= 0) {
                    imgDataOut = new byte[bufferInfo.size];

                    ByteBuffer outputBuffer = mediaCodecEncoder.getOutputBuffer(outputBufferIndex);
                    outputBuffer.get(imgDataOut);

                    frameIndex++;
                    decoderAndDisplay(imgDataOut, computePresentationTime(frameIndex));

                    mediaCodecEncoder.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mediaCodecEncoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);
                }

                pause(10);
            } catch (Exception e) {
                LogUtils.getInstance().e(TAG, e.getMessage());
            }
        }
    }

    private void pause(long time) {
        try
        {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            //
        }
    }

    /**
     * Generates the presentation time for frame N, in microseconds.
     */
    private long computePresentationTime(long frameIndex) {
        // 1秒 = 1000毫秒(milliseconds) = 1000000微秒(microseconds)
        // 1000000 / framerate 表示帧间间隔
        return 132 + frameIndex * 1000000 / framerate;
    }

    /**
     * 解码数据并显示
     * @param data
     * @param pts
     */
    private void decoderAndDisplay(byte[] data, long pts) {
        int inputBufferIndex = mediaCodecDecoder.dequeueInputBuffer(-1);
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = mediaCodecDecoder.getInputBuffer(inputBufferIndex);
            inputBuffer.clear();
            inputBuffer.put(data);
            mediaCodecDecoder.queueInputBuffer(inputBufferIndex, 0, data.length, pts, 0);
        }

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIndex = mediaCodecDecoder.dequeueOutputBuffer(bufferInfo,0);
        while (outputBufferIndex >= 0) {
            mediaCodecDecoder.releaseOutputBuffer(outputBufferIndex, true);
            outputBufferIndex = mediaCodecDecoder.dequeueOutputBuffer(bufferInfo, 0);
        }
    }

    public void stop(){
        if (!isRuning) {
            return;
        }

        try {
            isRuning = false;
            if (mediaCodecEncoder != null) {
                mediaCodecEncoder.stop();
                mediaCodecEncoder.release();
            }

            if (mediaCodecDecoder != null) {
                mediaCodecDecoder.stop();
                mediaCodecDecoder.release();
            }
        } catch (Exception e){
            LogUtils.getInstance().e(TAG, e.getMessage());
        } finally {
            mediaCodecEncoder = null;
            mediaCodecDecoder = null;
        }
    }
}
